#include <iostream.h>
#include "lista.h"

// Definirea constantei END
const ListaD::Iterator ListaD::END = nullptr;
 
void ListaD::push_front(int elem)
{
    // Daca lista este vida, atunci
    if(isEmpty())
    {
        head = new Nod; // Aloc memorie pentru primul nod
        head->data = elem;
        head->next = nullptr; // Fiind singurul nod, urmatorul este NIMIC adica NULL
        head->previous = nullptr; // Fiind singurul nod, anteriorul este NIMIC adica NULL
        tail = head; // si tail == head
    }
    else  // altfel
    {
        Nod * nod = new Nod; // Aloc memorie pentru noul nod
        nod->data = elem;    // Scriu informatia in data
        nod->previous = nullptr; // Devenind head, previous indica NULL
        nod->next = head;    // Leg nod de head
        head->previous = nod; // Predecesorul fostului head este 'nod'
        head = nod;          // nod devine noul head
    }
}
 
void ListaD::push_back(int elem)
{
    // Daca lista este vida, atunci
    if(isEmpty())
    {
        head = new Nod; // Aloc memorie pentru primul nod
        head->data = elem;
        head->next = nullptr; // Fiind singurul nod, urmatorul este NIMIC adica NULL
        head->previous = nullptr; // Fiind singurul nod, anteriorul este NIMIC adica NULL
        tail = head; // si tail == head
    }
    else  // altfel
    {
        Nod * nod = new Nod; // Aloc memorie pentru noul nod
        nod->data = elem;    // Scriu informatia in data
        nod->next = nullptr; // Devenind nod terminal, va fi legat de NULL
        nod->previous = tail; // previous indica tail
        tail->next = nod;    // Fostul tail este legat de noul tail
        tail = nod;          // nod devine tail
    }
}
 
void ListaD::insert_after(int elem, Iterator nod)
{
    Nod * newNod = new Nod; // Aloc memorie pentru noul nod
    newNod->data = elem;    // Scriu informatia in data
    newNod->next = nod.list->next; // newNod se leaga de succesorul lui 'nod'
    newNod->previous = nod.list; // Predecesorul lui newNod este 'nod'
    // Daca nodul 'nod' a fost ultimul nod, atunci nodul newNod devine nod terminal
    if(nod.list == tail) tail = newNod;
    else nod.list->next->previous = newNod; // Predecesorul succesorului lui 'nod' este newNod
    nod.list->next = newNod; // Nodul 'nod' se leaga de newNod
}
 
void ListaD::insert_before(int elem, Iterator nod)
{
    Nod * newNod = new Nod; // Aloc memorie pentru noul nod
    newNod->data = elem; // Scriu informatia in data
    newNod->next = nod.list; // Succesorul lui newNod este 'nod'
    newNod->previous = nod.list->previous; // Predecesorul lui newNod este predecesorul lui 'nod'
    // Daca nodul 'nod' a fost primul nod, atunci nodul newNod devine head
    if(nod.list == head) head = newNod;
    else nod.list->previous->next = newNod; // Succesorul predecesorului lui 'nod' este newNod
    nod.list->previous = newNod; // Predecesorul lui 'nod' este newNod
}
 
ListaD::Iterator ListaD::search(int value) const
{
    for(Nod* it = head; it != nullptr; it = it->next)
    {
        if(it->data == value) return Iterator(it); // Daca am gasit nodul il returnez
    }
    return END; // Nu am gasit nimic
}
 
void ListaD::pop_front()
{
    if(isEmpty()) throw "Empty List"; // Daca lista este vida
    if(head == tail) // Daca lista are un singur nod
    { delete head; head = tail = nullptr; return; }
    Nod * temp = head; // Salvez adresa obiectului head
    head->next->previous = nullptr; // Predecesorul succesorului lui head devine NULL
    head = head->next; // Succesorul lui head devine noul head
    delete temp; // Eliberez memoria ocupata de vechiul obiect head
}
 
void ListaD::remove(Iterator nod)
{
    if(isEmpty()) throw "Empty List"; // Daca lista este vida
    if(head == tail) // Daca lista are un singur nod
    { delete head; head = tail = nullptr; return; }
    nod.list->previous->next = nod.list->next; // Leg predecesorul lui 'nod' de succesorul acestuia
    nod.list->next->previous = nod.list->previous; // Predecesorul succesorului lui 'nod' indica predecesorul lui 'nod'
    delete nod.list; // Elimin nodul 'nod'
}
 
void ListaD::pop_back()
{
    if(isEmpty()) throw "Empty List"; // Daca lista este vida
    if(head == tail) // Daca lista are un singur nod
    { delete head; head = tail = nullptr; return; }
    Nod * temp = tail; // Salvez adresa obiectului tail
    tail->previous->next = nullptr; // Succesorul predecesorului lui tail devine NULL
    tail = tail->previous; // Predecesorul lui tail devine noul tail
    delete temp; // Eliberez memoria ocupata de vechiul obiect tail
}
 
void ListaD::clear()
{
    Nod *it = head, *temp;
    while(it != nullptr)
    {
        temp = it; // Salvez adresa nodului curent
        it = it->next; // Trec mai departe
        delete temp; // Distrug nodul curent
    }
    head = tail = nullptr; // Lista Vida
}

